/*-------------------------------------------------------------------------
 *
 * arrayutils.c
 *	  此文件包含一些用于数组函数的支持例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/arrayutils.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "catalog/pg_type.h"
#include "common/int.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/memutils.h"


/*
 * 将下标列表转换为线性元素编号（从 0 开始）
 *
 * 我们假设调用者已对维度和下标进行了范围检查，
 * 因此没有可能的溢出。
 */
int ArrayGetOffset(int fc_n, const int *fc_dim, const int *fc_lb, const int *fc_indx)
{
	int			fc_i,
				fc_scale = 1,
				fc_offset = 0;

	for (fc_i = fc_n - 1; fc_i >= 0; fc_i--)
	{
		fc_offset += (fc_indx[fc_i] - fc_lb[fc_i]) * fc_scale;
		fc_scale *= fc_dim[fc_i];
	}
	return fc_offset;
}

/*
 * 同上，但下标假定为0基，并使用比例数组
 * 而不是原始维度数据（请参见 mda_get_prod 创建比例数组）
 */
int ArrayGetOffset0(int fc_n, const int *fc_tup, const int *fc_scale)
{
	int			fc_i,
				fc_lin = 0;

	for (fc_i = 0; fc_i < fc_n; fc_i++)
		fc_lin += fc_tup[fc_i] * fc_scale[fc_i];
	return fc_lin;
}


/*
 * 将数组维度转换为元素数量
 *
 * 这必须进行溢出检查，因为它用于验证用户的
 * 维度请求不会超过我们可以处理的范围。
 *
 * 乘法溢出检查仅在具有 int64
 * 算术的机器上有效，但几乎所有平台现在都是这样，而对于那些没有的机器进行检查
 * 除法似乎成本太高。
 */
int ArrayGetNItems(int fc_ndim, const int *fc_dims)
{
	int32		fc_ret;
	int			fc_i;

	if (fc_ndim <= 0)
		return 0;
	fc_ret = 1;
	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
	{
		int64		fc_prod;

		/* 负维度意味着 UB-LB 溢出了... */
		if (fc_dims[fc_i] < 0)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("array size exceeds the maximum allowed (%d)",
							(int) MaxArraySize)));

		fc_prod = (int64) fc_ret * (int64) fc_dims[fc_i];

		fc_ret = (int32) fc_prod;
		if ((int64) fc_ret != fc_prod)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("array size exceeds the maximum allowed (%d)",
							(int) MaxArraySize)));
	}
	Assert(fc_ret >= 0);
	if ((Size) fc_ret > MaxArraySize)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("array size exceeds the maximum allowed (%d)",
						(int) MaxArraySize)));
	return (int) fc_ret;
}

/*
 * 验证数组拟议的下界值的有效性
 *
 * 下界值不能过大，以至于在计算下标时导致溢出，例如：下界 2147483640 与长度 10
 * 必须被禁止。我们实际上坚持 dims[i] + lb[i] 可以
 * 在不溢出的情况下计算，这意味着最后一个下标
 * 等于 INT_MAX 的数组将被禁止。
 *
 * 假设调用者已经调用了 ArrayGetNItems，因此
 * 溢出的（负的）dims[] 值已被消除。
 */
void ArrayCheckBounds(int fc_ndim, const int *fc_dims, const int *fc_lb)
{
	int			fc_i;

	for (fc_i = 0; fc_i < fc_ndim; fc_i++)
	{
		/* PG_USED_FOR_ASSERTS_ONLY 防止未读取变量的警告 */
		int32		fc_sum PG_USED_FOR_ASSERTS_ONLY;

		if (pg_add_s32_overflow(fc_dims[fc_i], fc_lb[fc_i], &fc_sum))
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("array lower bound is too large: %d",
							fc_lb[fc_i])));
	}
}

/*
 * 计算数组切片的范围（子数组维度）
 *
 * 我们假设调用者已验证切片端点，因此溢出是不可能的
 */
void mda_get_range(int fc_n, int *fc_span, const int *fc_st, const int *fc_endp)
{
	int			fc_i;

	for (fc_i = 0; fc_i < fc_n; fc_i++)
		fc_span[fc_i] = fc_endp[fc_i] - fc_st[fc_i] + 1;
}

/*
 * 计算数组维度的乘积，即下标的比例因子
 *
 * 我们假设调用者已验证维度，因此溢出是不可能的
 */
void mda_get_prod(int fc_n, const int *fc_range, int *fc_prod)
{
	int			fc_i;

	fc_prod[fc_n - 1] = 1;
	for (fc_i = fc_n - 2; fc_i >= 0; fc_i--)
		fc_prod[fc_i] = fc_prod[fc_i + 1] * fc_range[fc_i + 1];
}

/*
 * 从整个数组维度的乘积和子数组的跨度计算
 * 在数组内逐步通过子数组所需的偏移距离
 *
 * 我们假设调用者已验证维度，因此溢出是不可能的
 */
void mda_get_offset_values(int fc_n, int *fc_dist, const int *fc_prod, const int *fc_span)
{
	int			fc_i,
				fc_j;

	fc_dist[fc_n - 1] = 0;
	for (fc_j = fc_n - 2; fc_j >= 0; fc_j--)
	{
		fc_dist[fc_j] = fc_prod[fc_j] - 1;
		for (fc_i = fc_j + 1; fc_i < fc_n; fc_i++)
			fc_dist[fc_j] -= (fc_span[fc_i] - 1) * fc_prod[fc_i];
	}
}

/*
 * 生成的元组在字典顺序上比当前的
 * "curr" 中的 n-元组大1，限制是 "curr" 的第 i 个元素
 * 小于 "span" 的第 i 个元素。
 *
 * 如果没有下一个元组，则返回 -1，否则返回
 * 对应于要前进的维度的下标位置（0..n-1）。
 *
 * 我们假设调用者已验证维度，因此溢出是不可能的
 */
int mda_next_tuple(int fc_n, int *fc_curr, const int *fc_span)
{
	int			fc_i;

	if (fc_n <= 0)
		return -1;

	fc_curr[fc_n - 1] = (fc_curr[fc_n - 1] + 1) % fc_span[fc_n - 1];
	for (fc_i = fc_n - 1; fc_i && fc_curr[fc_i] == 0; fc_i--)
		fc_curr[fc_i - 1] = (fc_curr[fc_i - 1] + 1) % fc_span[fc_i - 1];

	if (fc_i)
		return fc_i;
	if (fc_curr[0])
		return 0;

	return -1;
}

/*
 * ArrayGetIntegerTypmods: 验证参数是否为 1-D cstring 数组，
 * 并将内容转换为整数。返回一个 palloc'd 数组
 * 并将长度放置在 *n 中。
 */
int32 *
ArrayGetIntegerTypmods(ArrayType *fc_arr, int *fc_n)
{
	int32	   *fc_result;
	Datum	   *fc_elem_values;
	int			fc_i;

	if (ARR_ELEMTYPE(fc_arr) != CSTRINGOID)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
				 errmsg("typmod array must be type cstring[]")));

	if (ARR_NDIM(fc_arr) != 1)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("typmod array must be one-dimensional")));

	if (array_contains_nulls(fc_arr))
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("typmod array must not contain nulls")));

	/* 关于 cstring 表示细节的硬编码知识在这里 */
	deconstruct_array(fc_arr, CSTRINGOID,
					  -2, false, TYPALIGN_CHAR,
					  &fc_elem_values, NULL, fc_n);

	fc_result = (int32 *) palloc(*fc_n * sizeof(int32));

	for (fc_i = 0; fc_i < *fc_n; fc_i++)
		fc_result[fc_i] = pg_strtoint32(DatumGetCString(fc_elem_values[fc_i]));

	pfree(fc_elem_values);

	return fc_result;
}
